/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.propagation.sampling;

import ore.AttitudeLaw;
import ore.Frame;
import ore.Orbit;
import ore.SpacecraftState;
import ore.errors.OrekitException;
import ore.errors.PropagationException;
import ore.orbits.EquinoctialOrbit;
import ore.propagation.numerical.ModeHandler;
import ore.time.AbsoluteDate;

import org.apache.commons.math.ode.DerivativeException;
import org.apache.commons.math.ode.sampling.StepHandler;
import org.apache.commons.math.ode.sampling.StepInterpolator;

/** 
 * Adapt an {@link ore.propagation.sampling.OrekitStepHandler} to
 * commons-math {@link StepHandler} interface.
 * 
 * @author Luc Maisonobe
 */
public class AdaptedStepHandler
    extends Object
    implements OrekitStepInterpolator, StepHandler, ModeHandler, 
               java.io.Serializable
{

    /** 
     * Reference date.
     */
    private AbsoluteDate initializedReference;
    /** 
     * Reference frame.
     */
    private Frame initializedFrame;
    /** 
     * Central body attraction coefficient.
     */
    private double initializedMu;
    /** 
     * Attitude law.
     */
    private AttitudeLaw initializedAttitudeLaw;
    /** 
     * Underlying handler.
     */
    private final OrekitStepHandler handler;
    /** 
     * Underlying raw rawInterpolator.
     */
    private StepInterpolator rawInterpolator;


    /** 
     * @param handler Required underlying handler to wrap
     */
    public AdaptedStepHandler(OrekitStepHandler handler) {
        super();
        this.handler = handler;
    }


    /**
     * {@inheritDoc}
     */
    public void initialize(AbsoluteDate reference, Frame frame,
                           double mu, AttitudeLaw attitudeLaw)
    {
        this.initializedReference   = reference;
        this.initializedFrame       = frame;
        this.initializedAttitudeLaw = attitudeLaw;
        this.initializedMu          = mu;
    }
    /** 
     * {@inheritDoc} 
     */
    public boolean requiresDenseOutput() {
        return this.handler.requiresDenseOutput();
    }
    /**
     * {@inheritDoc}
     */
    public void reset() {
        this.handler.reset();
    }
    /**
     * {@inheritDoc}
     */
    public void handleStep(StepInterpolator interpolator, boolean isLast)
        throws DerivativeException
    {
        try {
            this.rawInterpolator = interpolator;
            this.handler.handleStep(this, isLast);
        }
        catch (PropagationException pe) {
            throw new DerivativeException(pe);
        }
    }
    /** 
     * @return Current grid date
     */
    public AbsoluteDate getCurrentDate() {
        return new AbsoluteDate(this.initializedReference, this.rawInterpolator.getCurrentTime());
    }
    /** 
     * @return Previous grid date
     */
    public AbsoluteDate getPreviousDate() {
        return new AbsoluteDate(this.initializedReference, this.rawInterpolator.getPreviousTime());
    }
    /** 
     * If {@link #setInterpolatedDate(AbsoluteDate)
     * setInterpolatedDate} has not been called, the date returned is
     * the same as {@link #getCurrentDate() getCurrentDate}.
     * 
     * @return Interpolated date
     */
    public AbsoluteDate getInterpolatedDate() {
        return new AbsoluteDate(this.initializedReference, this.rawInterpolator.getInterpolatedTime());
    }
    /** 
     * It is possible to set the interpolation date outside of the
     * current step range, but accuracy will decrease as date is
     * farther.
     * 
     * @param date Interpolated date to set
     */
    public void setInterpolatedDate(AbsoluteDate date) {
        this.rawInterpolator.setInterpolatedTime(date.durationFrom(this.initializedReference));
    }
    /** 
     * @return Interpolated state at the current interpolation date
     * 
     * @exception OrekitException State cannot be interpolated or
     * converted
     * 
     * @see #getInterpolatedDate()
     * @see #setInterpolatedDate(AbsoluteDate)
     */
    public SpacecraftState getInterpolatedState() throws OrekitException {
        try {
            final double[] y = this.rawInterpolator.getInterpolatedState();

            final AbsoluteDate interpolatedDate =
                new AbsoluteDate(this.initializedReference, 
                                 this.rawInterpolator.getInterpolatedTime());

            double a    = y[0];
            double ex   = y[1];
            double ey   = y[2];
            double hx   = y[3];
            double hy   = y[4];
            double lv   = y[5];
            double mass = y[6];
            
            Orbit orbit = new EquinoctialOrbit(a, ex, ey, hx, hy, lv, 
                                               EquinoctialOrbit.TRUE_LATITUDE_ARGUMENT,
                                               this.initializedFrame, interpolatedDate, this.initializedMu);
            return new SpacecraftState(orbit,
                                       this.initializedAttitudeLaw.getState(interpolatedDate,
                                                                            orbit.getPositionVelocity(),
                                                                            this.initializedFrame),
                                       mass);
        }
        catch (DerivativeException de) {
            throw new PropagationException(de.getMessage(), de);
        }
    }
    /** 
     * @return Integration is forward in date
     */
    public boolean isForward() {
        return this.rawInterpolator.isForward();
    }
}
